// package 交易池
package txpool

import (
	"fmt"
	"osiris/core/state"
	"osiris/dto"
	"osiris/logger"
)

// TxPool 交易池接口
type TxPool interface {

	// Init 交易池初始化
	//  @param queuedSize
	//  @param pendingSize
	Init(queuedSize int, pendingSize int)

	// Close 交易池关闭
	Close()

	// AddTx 向交易池中添加交易
	//  @param tx
	//  @return bool 是否添加成功
	AddTx(tx dto.TxData) bool

	// inQueued 交易进queued的逻辑
	//  @param txData
	inQueued(txData *dto.TxData)

	// inPending 交易进pending的逻辑
	//  @param txData
	inPending(txData *dto.TxData)

	// IsTxInPool 判断交易是否在交易池
	//  @param fromAddrStr
	//  @param txHash
	//  @return bool
	IsTxInPool(fromAddrStr string, txHash string) bool

	// Pack 打包交易
	//  @return []dto.TxData
	Pack() []dto.TxData

	// Truncate 删除交易池中所有交易
	Truncate()

	// gc 交易池清理
	gc()
}

// TxDescendArray 根据nonce从大到小降序排列的Tx数组（fromAddr 是同一个账号）
type TxDescendArray struct {
	txs []*dto.TxData
}

// InsertTx 插入交易，按造nonce从大到小的顺序
//
//	@receiver array
//	@param txData
func (array *TxDescendArray) InsertTx(txData *dto.TxData) {
	if len(array.txs) == 0 {
		array.txs = []*dto.TxData{txData}
		return
	}

	inserted := false
	for index, tempTxData := range array.txs {
		if txData.Nonce > tempTxData.Nonce {
			//此时的index是第一个比txData.Nonce小的
			newTxs := array.txs[:index]
			newTxs = append(newTxs, txData)
			newTxs = append(newTxs, array.txs[index:]...)
			array.txs = newTxs
			break
		}
	}

	//txData的Nonce比数组里所有Tx都小
	if !inserted {
		array.txs = append(array.txs, txData)
	}
}

// CurrentNonce 数组中当前最小的nonce
//
//	@receiver array
//	@return int64
func (array *TxDescendArray) CurrentNonce() int64 {
	length := len(array.txs)
	if length == 0 {
		return state.InvalidNonce
	}

	return array.txs[length-1].Nonce
}

// PackStraightTxs 打包起始牌为startNonce的顺子（一个也算），并从array中删除
//
//	@receiver array Nonce降序交易数组
//	@param startNonce 起始随机数
//	@return []*dto.TxData 可以进pending的交易数组
//	@return bool 是否有可以进pending的交易
func (array *TxDescendArray) PackStraightTxs(startNonce int64) ([]*dto.TxData, bool) {
	//如果存在比startNonce小的交易，会把这些交易删了
	currentNonce := array.CurrentNonce()
	if currentNonce != state.InvalidNonce && currentNonce < startNonce {
		logger.Info(map[string]interface{}{fmt.Sprintf("[txpool] [TxDescendArray.StraightTxs()] start nonce: %d, current nonce: %d", startNonce, currentNonce): "exist out-of-date tx!"})

		index := -1
		for i := len(array.txs) - 1; i >= 0; i-- {
			if array.txs[i].Nonce >= startNonce {
				index = i
				break
			}
		}
		array.txs = array.txs[:index+1]
	}

	//没有起始牌为startNonce的顺子
	if array.CurrentNonce() > startNonce {
		return []*dto.TxData{}, false
	}

	//有起始牌为startNonce的顺子可以打包进pending
	var starightTxs []*dto.TxData
	end := len(array.txs)
	tempNonce := startNonce
	for i := len(array.txs) - 1; i >= 0; i-- {
		if tempNonce == array.txs[i].Nonce {
			starightTxs = append(starightTxs, array.txs[i])
			tempNonce += 1
			end = i
		} else {
			break
		}
	}

	//打包完毕后删除顺子
	array.txs = array.txs[:end]

	return starightTxs, true
}

// GetTxs 返回所有交易
//
//	@receiver array
//	@return []*dto.TxData
func (array *TxDescendArray) GetTxs() []*dto.TxData {
	return array.txs
}
